Imported Upstream version 2016.3
authorSimon McVittie <smcv@debian.org>
Fri, 11 Mar 2016 08:08:04 +0000 (08:08 +0000)
committerSimon McVittie <smcv@debian.org>
Fri, 11 Mar 2016 08:08:04 +0000 (08:08 +0000)
1  2 
libglnx/Makefile-libglnx.am
libglnx/glnx-alloca.h
libglnx/glnx-backport-autocleanups.h
libglnx/glnx-console.c
libglnx/glnx-console.h
libglnx/glnx-dirfd.c
libglnx/glnx-dirfd.h
libglnx/glnx-fdio.c
libglnx/glnx-fdio.h
libglnx/libglnx.h

index 712e931c0554802a296c607e62b376e89b13f400,0000000000000000000000000000000000000000..2e8da015365c29030c4791977bffe5f1df2e4644
mode 100644,000000..100644
--- /dev/null
@@@ -1,48 -1,0 +1,49 @@@
 +# Copyright (C) 2015 Colin Walters <walters@verbum.org>
 +#
 +# This library is free software; you can redistribute it and/or
 +# modify it under the terms of the GNU Lesser General Public
 +# License as published by the Free Software Foundation; either
 +# version 2 of the License, or (at your option) any later version.
 +#
 +# This library is distributed in the hope that it will be useful,
 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 +# Lesser General Public License for more details.
 +#
 +# You should have received a copy of the GNU Lesser General Public
 +# License along with this library; if not, write to the
 +# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 +# Boston, MA 02111-1307, USA.
 +
 +EXTRA_DIST += $(libglnx_srcpath)/README.md $(libglnx_srcpath)/COPYING
 +
 +libglnx_la_SOURCES = \
++      $(libglnx_srcpath)/glnx-alloca.h \
 +      $(libglnx_srcpath)/glnx-backport-autocleanups.h \
 +      $(libglnx_srcpath)/glnx-backport-autoptr.h \
 +      $(libglnx_srcpath)/glnx-backports.h \
 +      $(libglnx_srcpath)/glnx-backports.c \
 +      $(libglnx_srcpath)/glnx-local-alloc.h \
 +      $(libglnx_srcpath)/glnx-local-alloc.c \
 +      $(libglnx_srcpath)/glnx-errors.h \
 +      $(libglnx_srcpath)/glnx-errors.c \
 +      $(libglnx_srcpath)/glnx-console.h \
 +      $(libglnx_srcpath)/glnx-console.c \
 +      $(libglnx_srcpath)/glnx-dirfd.h \
 +      $(libglnx_srcpath)/glnx-dirfd.c \
 +      $(libglnx_srcpath)/glnx-fdio.h \
 +      $(libglnx_srcpath)/glnx-fdio.c \
 +      $(libglnx_srcpath)/glnx-lockfile.h \
 +      $(libglnx_srcpath)/glnx-lockfile.c \
 +      $(libglnx_srcpath)/glnx-libcontainer.h \
 +      $(libglnx_srcpath)/glnx-libcontainer.c \
 +      $(libglnx_srcpath)/glnx-xattrs.h \
 +      $(libglnx_srcpath)/glnx-xattrs.c \
 +      $(libglnx_srcpath)/glnx-shutil.h \
 +      $(libglnx_srcpath)/glnx-shutil.c \
 +      $(libglnx_srcpath)/libglnx.h \
 +      $(NULL)
 +
 +libglnx_la_CFLAGS = $(AM_CFLAGS) $(libglnx_cflags)
 +libglnx_la_LDFLAGS = -avoid-version -Bsymbolic-functions -export-symbols-regex "^glnx_" -no-undefined -export-dynamic 
 +libglnx_la_LIBADD = $(libglnx_libs)
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b0bf6a65abc03cae9dc091cf822b9280510736dd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
++ *
++ * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
++ * Boston, MA 02111-1307, USA.
++ */
++
++#pragma once
++
++#include <stdlib.h>
++#include <glib.h>
++
++G_BEGIN_DECLS
++
++/* Taken from https://github.com/systemd/systemd/src/basic/string-util.h
++ * at revision v228-666-gcf6c8c4
++ */
++#define glnx_strjoina(a, ...)                                           \
++        ({                                                              \
++                const char *_appendees_[] = { a, __VA_ARGS__ };         \
++                char *_d_, *_p_;                                        \
++                int _len_ = 0;                                          \
++                unsigned _i_;                                           \
++                for (_i_ = 0; _i_ < G_N_ELEMENTS(_appendees_) && _appendees_[_i_]; _i_++) \
++                        _len_ += strlen(_appendees_[_i_]);              \
++                _p_ = _d_ = alloca(_len_ + 1);                          \
++                for (_i_ = 0; _i_ < G_N_ELEMENTS(_appendees_) && _appendees_[_i_]; _i_++) \
++                        _p_ = stpcpy(_p_, _appendees_[_i_]);            \
++                *_p_ = 0;                                               \
++                _d_;                                                    \
++        })
++
++
++G_END_DECLS
index 53f7ffd21004d00a0d8086a9e17e741ea9339620,0000000000000000000000000000000000000000..cc2496114ea1094aaf7cd0329a19582a70759882
mode 100644,000000..100644
--- /dev/null
@@@ -1,117 -1,0 +1,124 @@@
- static inline void
- g_autoptr_cleanup_gstring_free (GString *string)
- {
-   if (string)
-     g_string_free (string, TRUE);
- }
 +/*
 + * Copyright © 2015 Canonical Limited
 + *
 + * This library is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2 of the licence, or (at your option) any later version.
 + *
 + * This library is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 + *
 + * Author: Ryan Lortie <desrt@desrt.ca>
 + */
 +
 +#pragma once
 +
 +#include <glnx-backport-autoptr.h>
 +
 +#if !GLIB_CHECK_VERSION(2, 43, 4)
 +
 +static inline void
 +g_autoptr_cleanup_generic_gfree (void *p)
 +{ 
 +  void **pp = (void**)p;
 +  if (*pp)
 +    g_free (*pp);
 +}
 +
- G_DEFINE_AUTOPTR_CLEANUP_FUNC(GString, g_autoptr_cleanup_gstring_free)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GAsyncQueue, g_async_queue_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBookmarkFile, g_bookmark_file_free)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GBytes, g_bytes_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GChecksum, g_checksum_free)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDateTime, g_date_time_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDir, g_dir_close)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GError, g_error_free)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GHashTable, g_hash_table_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GHmac, g_hmac_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GIOChannel, g_io_channel_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GKeyFile, g_key_file_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GList, g_list_free)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GArray, g_array_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GPtrArray, g_ptr_array_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainContext, g_main_context_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMainLoop, g_main_loop_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSource, g_source_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMappedFile, g_mapped_file_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMarkupParseContext, g_markup_parse_context_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(gchar, g_free)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GNode, g_node_destroy)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOptionContext, g_option_context_free)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOptionGroup, g_option_group_free)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GPatternSpec, g_pattern_spec_free)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GQueue, g_queue_free)
 +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GQueue, g_queue_clear)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GRand, g_rand_free)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GRegex, g_regex_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMatchInfo, g_match_info_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GScanner, g_scanner_destroy)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSequence, g_sequence_free)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSList, g_slist_free)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GStringChunk, g_string_chunk_free)
 +G_DEFINE_AUTO_CLEANUP_FREE_FUNC(GStrv, g_strfreev, NULL)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GThread, g_thread_unref)
 +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GMutex, g_mutex_clear)
 +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GCond, g_cond_clear)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTimer, g_timer_destroy)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTimeZone, g_time_zone_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTree, g_tree_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariant, g_variant_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantBuilder, g_variant_builder_unref)
 +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantBuilder, g_variant_builder_clear)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantIter, g_variant_iter_free)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantDict, g_variant_dict_unref)
 +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GVariantDict, g_variant_dict_clear)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GVariantType, g_variant_type_free)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSubprocess, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSubprocessLauncher, g_object_unref)
 +
 +/* Add GObject-based types as needed. */
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GCancellable, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverter, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GConverterOutputStream, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDataInputStream, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFile, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileEnumerator, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileIOStream, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileInfo, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileInputStream, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileMonitor, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFileOutputStream, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GInputStream, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMemoryInputStream, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GMemoryOutputStream, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GOutputStream, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSocket, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GSocketAddress, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTask, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsCertificate, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsDatabase, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GTlsInteraction, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusConnection, g_object_unref)
 +G_DEFINE_AUTOPTR_CLEANUP_FUNC(GDBusMessage, g_object_unref)
++G_DEFINE_AUTOPTR_CLEANUP_FUNC(GZlibCompressor, g_object_unref)
++G_DEFINE_AUTOPTR_CLEANUP_FUNC(GZlibDecompressor, g_object_unref)
++
++#endif
++
++#if !GLIB_CHECK_VERSION(2, 45, 8)
++
++static inline void
++g_autoptr_cleanup_gstring_free (GString *string)
++{
++  if (string)
++    g_string_free (string, TRUE);
++}
++
++G_DEFINE_AUTOPTR_CLEANUP_FUNC(GString, g_autoptr_cleanup_gstring_free)
 +
 +#endif
index deb4c86c32eafd3003242cd8164bc501c885b823,0000000000000000000000000000000000000000..397331186213ecbb5a1acd17a4c9a847d4a407e6
mode 100644,000000..100644
--- /dev/null
@@@ -1,289 -1,0 +1,289 @@@
-       fwrite (text, 1, textlen - 1, stdout);
 +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
 + *
 + * Copyright (C) 2013,2014,2015 Colin Walters <walters@verbum.org>
 + *
 + * This program is free software: you can redistribute it and/or modify
 + * it under the terms of the GNU Lesser General Public License as published
 + * by the Free Software Foundation; either version 2 of the licence or (at
 + * your option) any later version.
 + *
 + * This library is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General
 + * Public License along with this library; if not, write to the
 + * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 + * Boston, MA 02111-1307, USA.
 + */
 +
 +#include "config.h"
 +
 +#include "glnx-console.h"
 +
 +#include <unistd.h>
 +#include <string.h>
 +#include <fcntl.h>
 +#include <stdio.h>
 +#include <errno.h>
 +#include <sys/ioctl.h>
 +
 +static char *current_text = NULL;
 +static gint current_percent = -1;
 +static gboolean locked;
 +
 +static gboolean
 +stdout_is_tty (void)
 +{
 +  static gsize initialized = 0;
 +  static gboolean stdout_is_tty_v;
 +
 +  if (g_once_init_enter (&initialized))
 +    {
 +      stdout_is_tty_v = isatty (1);
 +      g_once_init_leave (&initialized, 1);
 +    }
 +
 +  return stdout_is_tty_v;
 +}
 +
 +static volatile guint cached_columns = 0;
 +static volatile guint cached_lines = 0;
 +
 +static int
 +fd_columns (int fd)
 +{
 +  struct winsize ws = {};
 +
 +  if (ioctl (fd, TIOCGWINSZ, &ws) < 0)
 +    return -errno;
 +
 +  if (ws.ws_col <= 0)
 +    return -EIO;
 +
 +  return ws.ws_col;
 +}
 +
 +/**
 + * glnx_console_columns:
 + * 
 + * Returns: The number of columns for terminal output
 + */
 +guint
 +glnx_console_columns (void)
 +{
 +  if (G_UNLIKELY (cached_columns == 0))
 +    {
 +      int c;
 +
 +      c = fd_columns (STDOUT_FILENO);
 +
 +      if (c <= 0)
 +        c = 80;
 +
 +      if (c > 256)
 +        c = 256;
 +
 +      cached_columns = c;
 +    }
 +
 +  return cached_columns;
 +}
 +
 +static int
 +fd_lines (int fd)
 +{
 +  struct winsize ws = {};
 +
 +  if (ioctl (fd, TIOCGWINSZ, &ws) < 0)
 +    return -errno;
 +
 +  if (ws.ws_row <= 0)
 +    return -EIO;
 +
 +  return ws.ws_row;
 +}
 +
 +/**
 + * glnx_console_lines:
 + * 
 + * Returns: The number of lines for terminal output
 + */
 +guint
 +glnx_console_lines (void)
 +{
 +  if (G_UNLIKELY (cached_lines == 0))
 +    {
 +      int l;
 +
 +      l = fd_lines (STDOUT_FILENO);
 +
 +      if (l <= 0)
 +        l = 24;
 +
 +      cached_lines = l;
 +    }
 +
 +  return cached_lines;
 +}
 +
 +static void
 +on_sigwinch (int signum)
 +{
 +  cached_columns = 0;
 +  cached_lines = 0;
 +}
 +
 +void
 +glnx_console_lock (GLnxConsoleRef *console)
 +{
 +  static gsize sigwinch_initialized = 0;
 +
 +  g_return_if_fail (!locked);
 +  g_return_if_fail (!console->locked);
 +
 +  console->is_tty = stdout_is_tty ();
 +
 +  locked = console->locked = TRUE;
 +
 +  current_percent = 0;
 +
 +  if (console->is_tty)
 +    {
 +      if (g_once_init_enter (&sigwinch_initialized))
 +        {
 +          signal (SIGWINCH, on_sigwinch);
 +          g_once_init_leave (&sigwinch_initialized, 1);
 +        }
 +      
 +      { static const char initbuf[] = { '\n', 0x1B, 0x37 };
 +        (void) fwrite (initbuf, 1, sizeof (initbuf), stdout);
 +      }
 +    }
 +}
 +
 +static void
 +printpad (const char *padbuf,
 +          guint       padbuf_len,
 +          guint       n)
 +{
 +  const guint d = n / padbuf_len;
 +  const guint r = n % padbuf_len;
 +  guint i;
 +
 +  for (i = 0; i < d; i++)
 +    fwrite (padbuf, 1, padbuf_len, stdout);
 +  fwrite (padbuf, 1, r, stdout);
 +}
 +
 +/**
 + * glnx_console_progress_text_percent:
 + * @text: Show this text before the progress bar
 + * @percentage: An integer in the range of 0 to 100
 + *
 + * On a tty, print to the console @text followed by an ASCII art
 + * progress bar whose percentage is @percentage.  If stdout is not a
 + * tty, a more basic line by line change will be printed.
 + *
 + * You must have called glnx_console_lock() before invoking this
 + * function.
 + *
 + */
 +void
 +glnx_console_progress_text_percent (const char *text,
 +                                    guint percentage)
 +{
 +  static const char equals[] = "====================";
 +  const guint n_equals = sizeof (equals) - 1;
 +  static const char spaces[] = "                    ";
 +  const guint n_spaces = sizeof (spaces) - 1;
 +  const guint ncolumns = glnx_console_columns ();
 +  const guint bar_min = 10;
 +  const guint input_textlen = text ? strlen (text) : 0;
 +  guint textlen;
 +  guint barlen;
 +
 +  g_return_if_fail (percentage >= 0 && percentage <= 100);
 +
 +  if (text && !*text)
 +    text = NULL;
 +
 +  if (percentage == current_percent
 +      && g_strcmp0 (text, current_text) == 0)
 +    return;
 +
 +  if (!stdout_is_tty ())
 +    {
 +      if (text)
 +        fprintf (stdout, "%s", text);
 +      if (percentage != -1)
 +        {
 +          if (text)
 +            fputc (' ', stdout);
 +          fprintf (stdout, "%u%%", percentage);
 +        }
 +      fputc ('\n', stdout);
 +      fflush (stdout);
 +      return;
 +    }
 +
 +  if (ncolumns < bar_min)
 +    return; /* TODO: spinner */
 +
 +  /* Restore cursor */
 +  { const char beginbuf[2] = { 0x1B, 0x38 };
 +    (void) fwrite (beginbuf, 1, sizeof (beginbuf), stdout);
 +  }
 +
 +  textlen = MIN (input_textlen, ncolumns - bar_min);
 +  barlen = ncolumns - textlen;
 +
 +  if (textlen > 0)
 +    {
-   locked = FALSE;
++      fwrite (text, 1, textlen, stdout);
 +      fputc (' ', stdout);
 +    }
 +  
 +  { 
 +    const guint nbraces = 2;
 +    const guint textpercent_len = 5;
 +    const guint bar_internal_len = barlen - nbraces - textpercent_len;
 +    const guint eqlen = bar_internal_len * (percentage / 100.0);
 +    const guint spacelen = bar_internal_len - eqlen; 
 +
 +    fputc ('[', stdout);
 +    printpad (equals, n_equals, eqlen);
 +    printpad (spaces, n_spaces, spacelen);
 +    fputc (']', stdout);
 +    fprintf (stdout, " %3d%%", percentage);
 +  }
 +
 +  { const guint spacelen = ncolumns - textlen - barlen;
 +    printpad (spaces, n_spaces, spacelen);
 +  }
 +
 +  fflush (stdout);
 +}
 +
 +/**
 + * glnx_console_unlock:
 + *
 + * Print a newline, and reset all cached console progress state.
 + *
 + * This function does nothing if stdout is not a tty.
 + */
 +void
 +glnx_console_unlock (GLnxConsoleRef *console)
 +{
 +  g_return_if_fail (locked);
 +  g_return_if_fail (console->locked);
 +
 +  current_percent = -1;
 +  g_clear_pointer (&current_text, g_free);
 +
 +  if (console->is_tty)
 +    fputc ('\n', stdout);
 +      
++  locked = console->locked = FALSE;
 +}
index 9f620cc7cc7baa079acb5315b023005c2105c59d,0000000000000000000000000000000000000000..8fc3865672f136488e45d0581f6580d62dbc8618
mode 100644,000000..100644
--- /dev/null
@@@ -1,52 -1,0 +1,53 @@@
-   glnx_console_unlock (p);
 +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
 + *
 + * Copyright (C) 2013,2014,2015 Colin Walters <walters@verbum.org>
 + *
 + * This program is free software: you can redistribute it and/or modify
 + * it under the terms of the GNU Lesser General Public License as published
 + * by the Free Software Foundation; either version 2 of the licence or (at
 + * your option) any later version.
 + *
 + * This library is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General
 + * Public License along with this library; if not, write to the
 + * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 + * Boston, MA 02111-1307, USA.
 + */
 +
 +#pragma once
 +
 +#include <glnx-backport-autocleanups.h>
 +
 +G_BEGIN_DECLS
 +
 +struct GLnxConsoleRef {
 +  gboolean locked;
 +  gboolean is_tty;
 +};
 +
 +typedef struct GLnxConsoleRef GLnxConsoleRef;
 +
 +void   glnx_console_lock (GLnxConsoleRef *ref);
 +
 +void   glnx_console_progress_text_percent (const char     *text,
 +                                             guint           percentage);
 +
 +void   glnx_console_unlock (GLnxConsoleRef *ref);
 +
 +guint    glnx_console_lines (void);
 +
 +guint    glnx_console_columns (void);
 +
 +static inline void
 +glnx_console_ref_cleanup (GLnxConsoleRef *p)
 +{
++  if (p->locked)
++    glnx_console_unlock (p);
 +}
 +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxConsoleRef, glnx_console_ref_cleanup)
 +
 +G_END_DECLS
index d126f15a09c7f486b53e501b15776f7277b52587,0000000000000000000000000000000000000000..4861ccfeffae512a89da182967034c9b9134fc6e
mode 100644,000000..100644
--- /dev/null
@@@ -1,305 -1,0 +1,354 @@@
 +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
 + *
 + * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
 + *
 + * This library is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2 of the License, or (at your option) any later version.
 + *
 + * This library is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with this library; if not, write to the
 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 + * Boston, MA 02111-1307, USA.
 + */
 +
 +#include "config.h"
 +
 +#include <string.h>
 +
 +#include <glnx-dirfd.h>
 +#include <glnx-errors.h>
 +#include <glnx-local-alloc.h>
 +
 +/**
 + * glnx_opendirat_with_errno:
 + * @dfd: File descriptor for origin directory
 + * @name: Pathname, relative to @dfd
 + * @follow: Whether or not to follow symbolic links
 + *
 + * Use openat() to open a directory, using a standard set of flags.
 + * This function sets errno.
 + */
 +int
 +glnx_opendirat_with_errno (int           dfd,
 +                           const char   *path,
 +                           gboolean      follow)
 +{
 +  int flags = O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY;
 +  if (!follow)
 +    flags |= O_NOFOLLOW;
 +
 +  dfd = glnx_dirfd_canonicalize (dfd);
 +
 +  return openat (dfd, path, flags);
 +}
 +
 +/**
 + * glnx_opendirat:
 + * @dfd: File descriptor for origin directory
 + * @path: Pathname, relative to @dfd
 + * @follow: Whether or not to follow symbolic links
 + * @error: Error
 + *
 + * Use openat() to open a directory, using a standard set of flags.
 + */
 +gboolean
 +glnx_opendirat (int             dfd,
 +                const char     *path,
 +                gboolean        follow,
 +                int            *out_fd,
 +                GError        **error)
 +{
 +  int ret = glnx_opendirat_with_errno (dfd, path, follow);
 +  if (ret == -1)
 +    {
 +      glnx_set_prefix_error_from_errno (error, "%s", "openat");
 +      return FALSE;
 +    }
 +  *out_fd = ret;
 +  return TRUE;
 +}
 +
 +struct GLnxRealDirfdIterator
 +{
 +  gboolean initialized;
 +  int fd;
 +  DIR *d;
 +};
 +typedef struct GLnxRealDirfdIterator GLnxRealDirfdIterator;
 +
 +/**
 + * glnx_dirfd_iterator_init_at:
 + * @dfd: File descriptor, may be AT_FDCWD or -1
 + * @path: Path, may be relative to @df
 + * @follow: If %TRUE and the last component of @path is a symlink, follow it
 + * @out_dfd_iter: (out caller-allocates): A directory iterator, will be initialized
 + * @error: Error
 + *
 + * Initialize @out_dfd_iter from @dfd and @path.
 + */
 +gboolean
 +glnx_dirfd_iterator_init_at (int                     dfd,
 +                             const char             *path,
 +                             gboolean                follow,
 +                             GLnxDirFdIterator      *out_dfd_iter,
 +                             GError                **error)
 +{
 +  gboolean ret = FALSE;
 +  glnx_fd_close int fd = -1;
 +  
 +  if (!glnx_opendirat (dfd, path, follow, &fd, error))
 +    goto out;
 +
 +  if (!glnx_dirfd_iterator_init_take_fd (fd, out_dfd_iter, error))
 +    goto out;
 +  fd = -1; /* Transfer ownership */
 +
 +  ret = TRUE;
 + out:
 +  return ret;
 +}
 +
 +/**
 + * glnx_dirfd_iterator_init_take_fd:
 + * @dfd: File descriptor - ownership is taken
 + * @dfd_iter: A directory iterator
 + * @error: Error
 + *
 + * Steal ownership of @dfd, using it to initialize @dfd_iter for
 + * iteration.
 + */
 +gboolean
 +glnx_dirfd_iterator_init_take_fd (int                dfd,
 +                                  GLnxDirFdIterator *dfd_iter,
 +                                  GError           **error)
 +{
 +  gboolean ret = FALSE;
 +  GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter;
 +  DIR *d = NULL;
 +
 +  d = fdopendir (dfd);
 +  if (!d)
 +    {
 +      glnx_set_prefix_error_from_errno (error, "%s", "fdopendir");
 +      goto out;
 +    }
 +
 +  real_dfd_iter->fd = dfd;
 +  real_dfd_iter->d = d;
 +
 +  ret = TRUE;
 + out:
 +  return ret;
 +}
 +
 +/**
 + * glnx_dirfd_iterator_next_dent:
 + * @dfd_iter: A directory iterator
 + * @out_dent: (out) (transfer none): Pointer to dirent; do not free
 + * @cancellable: Cancellable
 + * @error: Error
 + *
 + * Read the next value from @dfd_iter, causing @out_dent to be
 + * updated.  If end of stream is reached, @out_dent will be set
 + * to %NULL, and %TRUE will be returned.
 + */
 +gboolean
 +glnx_dirfd_iterator_next_dent (GLnxDirFdIterator  *dfd_iter,
 +                               struct dirent     **out_dent,
 +                               GCancellable       *cancellable,
 +                               GError             **error)
 +{
 +  gboolean ret = FALSE;
 +  GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter;
 +
++  g_return_val_if_fail (out_dent, FALSE);
++
 +  if (g_cancellable_set_error_if_cancelled (cancellable, error))
 +    goto out;
 +
 +  do
 +    {
 +      errno = 0;
 +      *out_dent = readdir (real_dfd_iter->d);
 +      if (*out_dent == NULL && errno != 0)
 +        {
 +          glnx_set_prefix_error_from_errno (error, "%s", "fdopendir");
 +          goto out;
 +        }
 +    } while (*out_dent &&
 +             (strcmp ((*out_dent)->d_name, ".") == 0 ||
 +              strcmp ((*out_dent)->d_name, "..") == 0));
 +
 +  ret = TRUE;
++ out:
++  return ret;
++}
++
++/**
++ * glnx_dirfd_iterator_next_dent_ensure_dtype:
++ * @dfd_iter: A directory iterator
++ * @out_dent: (out) (transfer none): Pointer to dirent; do not free
++ * @cancellable: Cancellable
++ * @error: Error
++ *
++ * A variant of @glnx_dirfd_iterator_next_dent, which will ensure the
++ * `dent->d_type` member is filled in by calling `fstatat`
++ * automatically if the underlying filesystem type sets `DT_UNKNOWN`.
++ */
++gboolean
++glnx_dirfd_iterator_next_dent_ensure_dtype (GLnxDirFdIterator  *dfd_iter,
++                                            struct dirent     **out_dent,
++                                            GCancellable       *cancellable,
++                                            GError            **error)
++{
++  gboolean ret = FALSE;
++  struct dirent *ret_dent;
++
++  g_return_val_if_fail (out_dent, FALSE);
++
++  if (!glnx_dirfd_iterator_next_dent (dfd_iter, out_dent, cancellable, error))
++    goto out;
++
++  ret_dent = *out_dent;
++
++  if (ret_dent)
++    {
++
++      if (ret_dent->d_type == DT_UNKNOWN)
++        {
++          struct stat stbuf;
++          if (TEMP_FAILURE_RETRY (fstatat (dfd_iter->fd, ret_dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW)) != 0)
++            {
++              glnx_set_error_from_errno (error);
++              goto out;
++            }
++          ret_dent->d_type = IFTODT (stbuf.st_mode);
++        }
++    }
++
++  ret = TRUE;
 + out:
 +  return ret;
 +}
 +
 +/**
 + * glnx_dirfd_iterator_clear:
 + * @dfd_iter: Iterator, will be de-initialized
 + *
 + * Unset @dfd_iter, freeing any resources.  If @dfd_iter is not
 + * initialized, do nothing.
 + */
 +void
 +glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter)
 +{
 +  GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter;
 +  /* fd is owned by dfd_iter */
 +  (void) closedir (real_dfd_iter->d);
 +  real_dfd_iter->initialized = FALSE;
 +}
 +
 +/**
 + * glnx_fdrel_abspath:
 + * @dfd: Directory fd
 + * @path: Path
 + *
 + * Turn a fd-relative pair into something that can be used for legacy
 + * APIs expecting absolute paths.
 + *
 + * This is Linux specific, and only valid inside this process (unless
 + * you set up the child process to have the exact same fd number, but
 + * don't try that).
 + */
 +char *
 +glnx_fdrel_abspath (int         dfd,
 +                    const char *path)
 +{
 +  dfd = glnx_dirfd_canonicalize (dfd);
 +  if (dfd == AT_FDCWD)
 +    return g_strdup (path);
 +  return g_strdup_printf ("/proc/self/fd/%d/%s", dfd, path);
 +}
 +
 +/**
 + * glnx_mkdtempat:
 + * @dfd: Directory fd
 + * @tmpl: (type filename): template directory name
 + * @mode: permissions to create the temporary directory with
 + * @error: Error
 + *
 + * Similar to g_mkdtemp_full, but using openat.
 + */
 +gboolean
 +glnx_mkdtempat (int dfd,
 +                gchar *tmpl,
 +                int mode,
 +                GError **error)
 +{
 +  char *XXXXXX;
 +  int count;
 +  static const char letters[] =
 +    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
 +  static const int NLETTERS = sizeof (letters) - 1;
 +  glong value;
 +  GTimeVal tv;
 +  static int counter = 0;
 +
 +  g_return_val_if_fail (tmpl != NULL, -1);
 +
 +  /* find the last occurrence of "XXXXXX" */
 +  XXXXXX = g_strrstr (tmpl, "XXXXXX");
 +
 +  if (!XXXXXX || strncmp (XXXXXX, "XXXXXX", 6))
 +    {
 +      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
 +                   "Invalid temporary directory template '%s'", tmpl);
 +      return FALSE;
 +    }
 +
 +  /* Get some more or less random data.  */
 +  g_get_current_time (&tv);
 +  value = (tv.tv_usec ^ tv.tv_sec) + counter++;
 +
 +  for (count = 0; count < 100; value += 7777, ++count)
 +    {
 +      glong v = value;
 +
 +      /* Fill in the random bits.  */
 +      XXXXXX[0] = letters[v % NLETTERS];
 +      v /= NLETTERS;
 +      XXXXXX[1] = letters[v % NLETTERS];
 +      v /= NLETTERS;
 +      XXXXXX[2] = letters[v % NLETTERS];
 +      v /= NLETTERS;
 +      XXXXXX[3] = letters[v % NLETTERS];
 +      v /= NLETTERS;
 +      XXXXXX[4] = letters[v % NLETTERS];
 +      v /= NLETTERS;
 +      XXXXXX[5] = letters[v % NLETTERS];
 +
 +      if (mkdirat (dfd, tmpl, mode) == -1)
 +        {
 +          if (errno == EEXIST)
 +            continue;
 +
 +          /* Any other error will apply also to other names we might
 +           *  try, and there are 2^32 or so of them, so give up now.
 +           */
 +          glnx_set_prefix_error_from_errno (error, "%s", "mkdirat");
 +          return FALSE;
 +        }
 +
 +      return TRUE;
 +    }
 +
 +  g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS,
 +               "mkstempat ran out of combinations to try.");
 +  return FALSE;
 +}
index 5b7b77a94d12ed58809ab534e95f2772e098900f,0000000000000000000000000000000000000000..c13e3dc5d9d7caaff67092db8130cccbbae15ffb
mode 100644,000000..100644
--- /dev/null
@@@ -1,85 -1,0 +1,89 @@@
 +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
 + *
 + * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
 + *
 + * This library is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2 of the License, or (at your option) any later version.
 + *
 + * This library is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with this library; if not, write to the
 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 + * Boston, MA 02111-1307, USA.
 + */
 +
 +#pragma once
 +
 +#include <glnx-backport-autocleanups.h>
 +#include <limits.h>
 +#include <dirent.h>
 +#include <sys/stat.h>
 +#include <fcntl.h>
 +
 +G_BEGIN_DECLS
 + 
 +/**
 + * glnx_dirfd_canonicalize:
 + * @fd: A directory file descriptor
 + *
 + * It's often convenient in programs to use `-1` for "unassigned fd",
 + * and also because gobject-introspection doesn't support `AT_FDCWD`,
 + * libglnx honors `-1` to mean `AT_FDCWD`.  This small inline function
 + * canonicalizes `-1 -> AT_FDCWD`.
 + */
 +static inline int
 +glnx_dirfd_canonicalize (int fd)
 +{
 +  if (fd == -1)
 +    return AT_FDCWD;
 +  return fd;
 +}
 +
 +struct GLnxDirFdIterator {
 +  gboolean initialized;
 +  int fd;
 +  gpointer padding_data[4];
 +};
 +
 +typedef struct GLnxDirFdIterator GLnxDirFdIterator;
 +gboolean glnx_dirfd_iterator_init_at (int dfd, const char *path,
 +                                    gboolean follow,
 +                                    GLnxDirFdIterator *dfd_iter, GError **error);
 +gboolean glnx_dirfd_iterator_init_take_fd (int dfd, GLnxDirFdIterator *dfd_iter, GError **error);
 +gboolean glnx_dirfd_iterator_next_dent (GLnxDirFdIterator  *dfd_iter,
 +                                        struct dirent     **out_dent,
 +                                        GCancellable       *cancellable,
 +                                        GError            **error);
++gboolean glnx_dirfd_iterator_next_dent_ensure_dtype (GLnxDirFdIterator  *dfd_iter,
++                                                     struct dirent     **out_dent,
++                                                     GCancellable       *cancellable,
++                                                     GError            **error);
 +void glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter);
 +
 +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxDirFdIterator, glnx_dirfd_iterator_clear)
 +
 +int glnx_opendirat_with_errno (int           dfd,
 +                               const char   *path,
 +                               gboolean      follow);
 +
 +gboolean glnx_opendirat (int             dfd,
 +                         const char     *path,
 +                         gboolean        follow,
 +                         int            *out_fd,
 +                         GError        **error);
 +
 +char *glnx_fdrel_abspath (int         dfd,
 +                          const char *path);
 +
 +gboolean glnx_mkdtempat (int dfd,
 +                         gchar *tmpl,
 +                         int mode,
 +                         GError **error);
 +
 +G_END_DECLS
index 7db33c4c61e45ef7ed105bde15d10296047c18fa,0000000000000000000000000000000000000000..466cbc4e82e472e230b3739b5bcee86776865eee
mode 100644,000000..100644
--- /dev/null
@@@ -1,750 -1,0 +1,750 @@@
- static int loop_write(int fd, const void *buf, size_t nbytes) {
 +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
 + *
 + * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
 + *
 + * Portions derived from systemd:
 + *  Copyright 2010 Lennart Poettering
 + *
 + * This library is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2 of the License, or (at your option) any later version.
 + *
 + * This library is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with this library; if not, write to the
 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 + * Boston, MA 02111-1307, USA.
 + */
 +
 +#include "config.h"
 +
 +#include <string.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <stdint.h>
 +#include <stdbool.h>
 +#include <sys/ioctl.h>
 +#include <sys/sendfile.h>
 +#include <errno.h>
 +/* See linux.git/fs/btrfs/ioctl.h */
 +#define BTRFS_IOCTL_MAGIC 0x94
 +#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int)
 +
 +#include <glnx-fdio.h>
 +#include <glnx-dirfd.h>
 +#include <glnx-errors.h>
 +#include <glnx-xattrs.h>
 +#include <glnx-backport-autoptr.h>
 +#include <glnx-local-alloc.h>
 +
 +static guint8*
 +glnx_fd_readall_malloc (int               fd,
 +                        gsize            *out_len,
 +                        gboolean          nul_terminate,
 +                        GCancellable     *cancellable,
 +                        GError          **error)
 +{
 +  gboolean success = FALSE;
 +  const guint maxreadlen = 4096;
 +  int res;
 +  struct stat stbuf;
 +  guint8* buf = NULL;
 +  gsize buf_allocated;
 +  gsize buf_size = 0;
 +  gssize bytes_read;
 +
 +  do
 +    res = fstat (fd, &stbuf);
 +  while (G_UNLIKELY (res == -1 && errno == EINTR));
 +  if (res == -1)
 +    {
 +      glnx_set_error_from_errno (error);
 +      goto out;
 +    }
 +
 +  if (S_ISREG (stbuf.st_mode) && stbuf.st_size > 0)
 +    buf_allocated = stbuf.st_size;
 +  else
 +    buf_allocated = 16;
 +        
 +  buf = g_malloc (buf_allocated);
 +
 +  while (TRUE)
 +    {
 +      gsize readlen = MIN (buf_allocated - buf_size, maxreadlen);
 +      
 +      if (g_cancellable_set_error_if_cancelled (cancellable, error))
 +        goto out;
 +
 +      do
 +        bytes_read = read (fd, buf + buf_size, readlen);
 +      while (G_UNLIKELY (bytes_read == -1 && errno == EINTR));
 +      if (G_UNLIKELY (bytes_read == -1))
 +        {
 +          glnx_set_error_from_errno (error);
 +          goto out;
 +        }
 +      if (bytes_read == 0)
 +        break;
 +      
 +      buf_size += bytes_read;
 +      if (buf_allocated - buf_size < maxreadlen)
 +        buf = g_realloc (buf, buf_allocated *= 2);
 +    }
 +
 +  if (nul_terminate)
 +    {
 +      if (buf_allocated - buf_size == 0)
 +        buf = g_realloc (buf, buf_allocated + 1);
 +      buf[buf_size] = '\0';
 +    }
 +
 +  success = TRUE;
 + out:
 +  if (success)
 +    {
 +      *out_len = buf_size;
 +      return buf;
 +    }
 +  g_free (buf);
 +  return NULL;
 +}
 +
 +/**
 + * glnx_fd_readall_bytes:
 + * @fd: A file descriptor
 + * @cancellable: Cancellable:
 + * @error: Error
 + *
 + * Read all data from file descriptor @fd into a #GBytes.  It's
 + * recommended to only use this for small files.
 + *
 + * Returns: (transfer full): A newly allocated #GBytes
 + */
 +GBytes *
 +glnx_fd_readall_bytes (int               fd,
 +                       GCancellable     *cancellable,
 +                       GError          **error)
 +{
 +  guint8 *buf;
 +  gsize len;
 +  
 +  buf = glnx_fd_readall_malloc (fd, &len, FALSE, cancellable, error);
 +  if (!buf)
 +    return NULL;
 +  
 +  return g_bytes_new_take (buf, len);
 +}
 +
 +/**
 + * glnx_fd_readall_utf8:
 + * @fd: A file descriptor
 + * @out_len: (out): Returned length
 + * @cancellable: Cancellable:
 + * @error: Error
 + *
 + * Read all data from file descriptor @fd, validating
 + * the result as UTF-8.
 + *
 + * Returns: (transfer full): A string validated as UTF-8, or %NULL on error.
 + */
 +char *
 +glnx_fd_readall_utf8 (int               fd,
 +                      gsize            *out_len,
 +                      GCancellable     *cancellable,
 +                      GError          **error)
 +{
 +  gboolean success = FALSE;
 +  guint8 *buf;
 +  gsize len;
 +  
 +  buf = glnx_fd_readall_malloc (fd, &len, TRUE, cancellable, error);
 +  if (!buf)
 +    goto out;
 +
 +  if (!g_utf8_validate ((char*)buf, len, NULL))
 +    {
 +      g_set_error (error,
 +                   G_IO_ERROR,
 +                   G_IO_ERROR_INVALID_DATA,
 +                   "Invalid UTF-8");
 +      goto out;
 +    }
 +
 +  success = TRUE;
 + out:
 +  if (success)
 +    {
 +      if (out_len)
 +        *out_len = len;
 +      return (char*)buf;
 +    }
 +  g_free (buf);
 +  return NULL;
 +}
 +
 +/**
 + * glnx_file_get_contents_utf8_at:
 + * @dfd: Directory file descriptor
 + * @subpath: Path relative to @dfd
 + * @out_len: (out) (allow-none): Optional length
 + * @cancellable: Cancellable
 + * @error: Error
 + *
 + * Read the entire contents of the file referred
 + * to by @dfd and @subpath, validate the result as UTF-8.
 + * The length is optionally stored in @out_len.
 + *
 + * Returns: (transfer full): UTF-8 validated text, or %NULL on error
 + */
 +char *
 +glnx_file_get_contents_utf8_at (int                   dfd,
 +                                const char           *subpath,
 +                                gsize                *out_len,
 +                                GCancellable         *cancellable,
 +                                GError              **error)
 +{
 +  gboolean success = FALSE;
 +  glnx_fd_close int fd = -1;
 +  char *buf = NULL;
 +  gsize len;
 +
 +  dfd = glnx_dirfd_canonicalize (dfd);
 +
 +  do
 +    fd = openat (dfd, subpath, O_RDONLY | O_NOCTTY | O_CLOEXEC);
 +  while (G_UNLIKELY (fd == -1 && errno == EINTR));
 +  if (G_UNLIKELY (fd == -1))
 +    {
 +      glnx_set_error_from_errno (error);
 +      goto out;
 +    }
 +
 +  buf = glnx_fd_readall_utf8 (fd, &len, cancellable, error);
 +  if (G_UNLIKELY(!buf))
 +    goto out;
 +  
 +  success = TRUE;
 + out:
 +  if (success)
 +    {
 +      if (out_len)
 +        *out_len = len;
 +      return buf;
 +    }
 +  g_free (buf);
 +  return NULL;
 +}
 +
 +/**
 + * glnx_readlinkat_malloc:
 + * @dfd: Directory file descriptor
 + * @subpath: Subpath
 + * @cancellable: Cancellable
 + * @error: Error
 + *
 + * Read the value of a symlink into a dynamically
 + * allocated buffer.
 + */
 +char *
 +glnx_readlinkat_malloc (int            dfd,
 +                        const char    *subpath,
 +                        GCancellable  *cancellable,
 +                        GError       **error)
 +{
 +  size_t l = 100;
 +
 +  dfd = glnx_dirfd_canonicalize (dfd);
 +
 +  for (;;)
 +    {
 +      char *c;
 +      ssize_t n;
 +
 +      c = g_malloc (l);
 +      n = TEMP_FAILURE_RETRY (readlinkat (dfd, subpath, c, l-1));
 +      if (n < 0)
 +        {
 +          glnx_set_error_from_errno (error);
 +          g_free (c);
 +          return FALSE;
 +        }
 +
 +      if ((size_t) n < l-1)
 +        {
 +          c[n] = 0;
 +          return c;
 +        }
 +
 +      g_free (c);
 +      l *= 2;
 +    }
 +
 +  g_assert_not_reached ();
 +}
 +
 +static gboolean
 +copy_symlink_at (int                   src_dfd,
 +                 const char           *src_subpath,
 +                 const struct stat    *src_stbuf,
 +                 int                   dest_dfd,
 +                 const char           *dest_subpath,
 +                 GLnxFileCopyFlags     copyflags,
 +                 GCancellable         *cancellable,
 +                 GError              **error)
 +{
 +  gboolean ret = FALSE;
 +  g_autofree char *buf = NULL;
 +
 +  buf = glnx_readlinkat_malloc (src_dfd, src_subpath, cancellable, error);
 +  if (!buf)
 +    goto out;
 +
 +  if (TEMP_FAILURE_RETRY (symlinkat (buf, dest_dfd, dest_subpath)) != 0)
 +    {
 +      glnx_set_error_from_errno (error);
 +      goto out;
 +    }
 +  
 +  if (!(copyflags & GLNX_FILE_COPY_NOXATTRS))
 +    {
 +      g_autoptr(GVariant) xattrs = NULL;
 +
 +      if (!glnx_dfd_name_get_all_xattrs (src_dfd, src_subpath, &xattrs,
 +                                         cancellable, error))
 +        goto out;
 +
 +      if (!glnx_dfd_name_set_all_xattrs (dest_dfd, dest_subpath, xattrs,
 +                                         cancellable, error))
 +        goto out;
 +    }
 +  
 +  if (TEMP_FAILURE_RETRY (fchownat (dest_dfd, dest_subpath,
 +                                    src_stbuf->st_uid, src_stbuf->st_gid,
 +                                    AT_SYMLINK_NOFOLLOW)) != 0)
 +    {
 +      glnx_set_error_from_errno (error);
 +      goto out;
 +    }
 +
 +  ret = TRUE;
 + out:
 +  return ret;
 +}
 +
 +#define COPY_BUFFER_SIZE (16*1024)
 +
 +/* From systemd */
 +
 +static int btrfs_reflink(int infd, int outfd) {
 +        int r;
 +
 +        g_return_val_if_fail(infd >= 0, -1);
 +        g_return_val_if_fail(outfd >= 0, -1);
 +
 +        r = ioctl(outfd, BTRFS_IOC_CLONE, infd);
 +        if (r < 0)
 +                return -errno;
 +
 +        return 0;
 +}
 +
-                         r = loop_write(fdt, buf, (size_t) n);
++int glnx_loop_write(int fd, const void *buf, size_t nbytes) {
 +        const uint8_t *p = buf;
 +
 +        g_return_val_if_fail(fd >= 0, -1);
 +        g_return_val_if_fail(buf, -1);
 +
 +        errno = 0;
 +
 +        while (nbytes > 0) {
 +                ssize_t k;
 +
 +                k = write(fd, p, nbytes);
 +                if (k < 0) {
 +                        if (errno == EINTR)
 +                                continue;
 +
 +                        return -errno;
 +                }
 +
 +                if (k == 0) /* Can't really happen */
 +                        return -EIO;
 +
 +                p += k;
 +                nbytes -= k;
 +        }
 +
 +        return 0;
 +}
 +
 +static int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
 +        bool try_sendfile = true;
 +        int r;
 +
 +        g_return_val_if_fail (fdf >= 0, -1);
 +        g_return_val_if_fail (fdt >= 0, -1);
 +
 +        /* Try btrfs reflinks first. */
 +        if (try_reflink && max_bytes == (off_t) -1) {
 +                r = btrfs_reflink(fdf, fdt);
 +                if (r >= 0)
 +                        return r;
 +        }
 +
 +        for (;;) {
 +                size_t m = COPY_BUFFER_SIZE;
 +                ssize_t n;
 +
 +                if (max_bytes != (off_t) -1) {
 +
 +                        if (max_bytes <= 0)
 +                                return -EFBIG;
 +
 +                        if ((off_t) m > max_bytes)
 +                                m = (size_t) max_bytes;
 +                }
 +
 +                /* First try sendfile(), unless we already tried */
 +                if (try_sendfile) {
 +
 +                        n = sendfile(fdt, fdf, NULL, m);
 +                        if (n < 0) {
 +                                if (errno != EINVAL && errno != ENOSYS)
 +                                        return -errno;
 +
 +                                try_sendfile = false;
 +                                /* use fallback below */
 +                        } else if (n == 0) /* EOF */
 +                                break;
 +                        else if (n > 0)
 +                                /* Succcess! */
 +                                goto next;
 +                }
 +
 +                /* As a fallback just copy bits by hand */
 +                {
 +                        char buf[m];
 +
 +                        n = read(fdf, buf, m);
 +                        if (n < 0)
 +                                return -errno;
 +                        if (n == 0) /* EOF */
 +                                break;
 +
-   if ((r = loop_write (fd, buf, len)) != 0)
++                        r = glnx_loop_write(fdt, buf, (size_t) n);
 +                        if (r < 0)
 +                                return r;
 +                }
 +
 +        next:
 +                if (max_bytes != (off_t) -1) {
 +                        g_assert(max_bytes >= n);
 +                        max_bytes -= n;
 +                }
 +        }
 +
 +        return 0;
 +}
 +
 +/**
 + * glnx_file_copy_at:
 + * @src_dfd: Source directory fd
 + * @src_subpath: Subpath relative to @src_dfd
 + * @dest_dfd: Target directory fd
 + * @dest_subpath: Destination name
 + * @copyflags: Flags
 + * @cancellable: cancellable
 + * @error: Error
 + *
 + * Perform a full copy of the regular file or
 + * symbolic link from @src_subpath to @dest_subpath.
 + *
 + * If @src_subpath is anything other than a regular
 + * file or symbolic link, an error will be returned.
 + */
 +gboolean
 +glnx_file_copy_at (int                   src_dfd,
 +                   const char           *src_subpath,
 +                   struct stat          *src_stbuf,
 +                   int                   dest_dfd,
 +                   const char           *dest_subpath,
 +                   GLnxFileCopyFlags     copyflags,
 +                   GCancellable         *cancellable,
 +                   GError              **error)
 +{
 +  gboolean ret = FALSE;
 +  int r;
 +  int dest_open_flags;
 +  struct timespec ts[2];
 +  glnx_fd_close int src_fd = -1;
 +  glnx_fd_close int dest_fd = -1;
 +  struct stat local_stbuf;
 +
 +  if (g_cancellable_set_error_if_cancelled (cancellable, error))
 +    goto out;
 +
 +  src_dfd = glnx_dirfd_canonicalize (src_dfd);
 +  dest_dfd = glnx_dirfd_canonicalize (dest_dfd);
 +
 +  /* Automatically do stat() if no stat buffer was supplied */
 +  if (!src_stbuf)
 +    {
 +      if (fstatat (src_dfd, src_subpath, &local_stbuf, AT_SYMLINK_NOFOLLOW) != 0)
 +        {
 +          glnx_set_error_from_errno (error);
 +          goto out;
 +        }
 +      src_stbuf = &local_stbuf;
 +    }
 +
 +  if (S_ISLNK (src_stbuf->st_mode))
 +    {
 +      return copy_symlink_at (src_dfd, src_subpath, src_stbuf,
 +                              dest_dfd, dest_subpath,
 +                              copyflags,
 +                              cancellable, error);
 +    }
 +  else if (!S_ISREG (src_stbuf->st_mode))
 +    {
 +      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
 +                   "Cannot copy non-regular/non-symlink file: %s", src_subpath);
 +      goto out;
 +    }
 +
 +  src_fd = TEMP_FAILURE_RETRY (openat (src_dfd, src_subpath, O_RDONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW));
 +  if (src_fd == -1)
 +    {
 +      glnx_set_error_from_errno (error);
 +      goto out;
 +    }
 +
 +  dest_open_flags = O_WRONLY | O_CREAT | O_CLOEXEC | O_NOCTTY;
 +  if (!(copyflags & GLNX_FILE_COPY_OVERWRITE))
 +    dest_open_flags |= O_EXCL;
 +  else
 +    dest_open_flags |= O_TRUNC;
 +
 +  dest_fd = TEMP_FAILURE_RETRY (openat (dest_dfd, dest_subpath, dest_open_flags, src_stbuf->st_mode));
 +  if (dest_fd == -1)
 +    {
 +      glnx_set_error_from_errno (error);
 +      goto out;
 +    }
 +
 +  r = copy_bytes (src_fd, dest_fd, (off_t) -1, TRUE);
 +  if (r < 0)
 +    {
 +      errno = -r;
 +      glnx_set_error_from_errno (error);
 +      goto out;
 +    }
 +
 +  if (fchown (dest_fd, src_stbuf->st_uid, src_stbuf->st_gid) != 0)
 +    {
 +      glnx_set_error_from_errno (error);
 +      goto out;
 +    }
 +
 +  if (fchmod (dest_fd, src_stbuf->st_mode & 07777) != 0)
 +    {
 +      glnx_set_error_from_errno (error);
 +      goto out;
 +    }
 +
 +  ts[0] = src_stbuf->st_atim;
 +  ts[1] = src_stbuf->st_mtim;
 +  (void) futimens (dest_fd, ts);
 +
 +  if (!(copyflags & GLNX_FILE_COPY_NOXATTRS))
 +    {
 +      g_autoptr(GVariant) xattrs = NULL;
 +
 +      if (!glnx_fd_get_all_xattrs (src_fd, &xattrs,
 +                                   cancellable, error))
 +        goto out;
 +
 +      if (!glnx_fd_set_all_xattrs (dest_fd, xattrs,
 +                                   cancellable, error))
 +        goto out;
 +    }
 +
 +  if (copyflags & GLNX_FILE_COPY_DATASYNC)
 +    {
 +      if (fdatasync (dest_fd) < 0)
 +        {
 +          glnx_set_error_from_errno (error);
 +          goto out;
 +        }
 +    }
 +  
 +  r = close (dest_fd);
 +  dest_fd = -1;
 +  if (r < 0)
 +    {
 +      glnx_set_error_from_errno (error);
 +      goto out;
 +    }
 +
 +  ret = TRUE;
 + out:
 +  if (!ret)
 +    (void) unlinkat (dest_dfd, dest_subpath, 0);
 +  return ret;
 +}
 +
 +/**
 + * glnx_file_replace_contents_at:
 + * @dfd: Directory fd
 + * @subpath: Subpath
 + * @buf: (array len=len) (element-type guint8): File contents
 + * @len: Length (if `-1`, assume @buf is `NUL` terminated)
 + * @flags: Flags
 + * @cancellable: Cancellable
 + * @error: Error
 + *
 + * Create a new file, atomically replacing the contents of @subpath
 + * (relative to @dfd) with @buf.  By default, if the file already
 + * existed, fdatasync() will be used before rename() to ensure stable
 + * contents.  This and other behavior can be controlled via @flags.
 + *
 + * Note that no metadata from the existing file is preserved, such as
 + * uid/gid or extended attributes.  The default mode will be `0666`,
 + * modified by umask.
 + */ 
 +gboolean
 +glnx_file_replace_contents_at (int                   dfd,
 +                               const char           *subpath,
 +                               const guint8         *buf,
 +                               gsize                 len,
 +                               GLnxFileReplaceFlags  flags,
 +                               GCancellable         *cancellable,
 +                               GError              **error)
 +{
 +  return glnx_file_replace_contents_with_perms_at (dfd, subpath, buf, len,
 +                                                   (mode_t) -1, (uid_t) -1, (gid_t) -1,
 +                                                   flags, cancellable, error);
 +                                                   
 +}
 +
 +/**
 + * glnx_file_replace_contents_with_perms_at:
 + * @dfd: Directory fd
 + * @subpath: Subpath
 + * @buf: (array len=len) (element-type guint8): File contents
 + * @len: Length (if `-1`, assume @buf is `NUL` terminated)
 + * @mode: File mode; if `-1`, use `0666 - umask`
 + * @flags: Flags
 + * @cancellable: Cancellable
 + * @error: Error
 + *
 + * Like glnx_file_replace_contents_at(), but also supports
 + * setting mode, and uid/gid.
 + */ 
 +gboolean
 +glnx_file_replace_contents_with_perms_at (int                   dfd,
 +                                          const char           *subpath,
 +                                          const guint8         *buf,
 +                                          gsize                 len,
 +                                          mode_t                mode,
 +                                          uid_t                 uid,
 +                                          gid_t                 gid,
 +                                          GLnxFileReplaceFlags  flags,
 +                                          GCancellable         *cancellable,
 +                                          GError              **error)
 +{
 +  gboolean ret = FALSE;
 +  int r;
 +  /* We use the /proc/self trick as there's no mkostemp_at() yet */
 +  g_autofree char *tmppath = g_strdup_printf ("/proc/self/fd/%d/.tmpXXXXXX", dfd);
 +  glnx_fd_close int fd = -1;
 +
 +  dfd = glnx_dirfd_canonicalize (dfd);
 +
 +  if ((fd = g_mkstemp_full (tmppath, O_WRONLY | O_CLOEXEC,
 +                            mode == (mode_t) -1 ? 0666 : mode)) == -1)
 +    {
 +      glnx_set_error_from_errno (error);
 +      goto out;
 +    }
 +
 +  if (len == -1)
 +    len = strlen ((char*)buf);
 +
 +  /* Note that posix_fallocate does *not* set errno but returns it. */
 +  r = posix_fallocate (fd, 0, len);
 +  if (r != 0)
 +    {
 +      errno = r;
 +      glnx_set_error_from_errno (error);
 +      goto out;
 +    }
 +
++  if ((r = glnx_loop_write (fd, buf, len)) != 0)
 +    {
 +      errno = -r;
 +      glnx_set_error_from_errno (error);
 +      goto out;
 +    }
 +    
 +  if (!(flags & GLNX_FILE_REPLACE_NODATASYNC))
 +    {
 +      struct stat stbuf;
 +      gboolean do_sync;
 +      
 +      if (fstatat (dfd, subpath, &stbuf, AT_SYMLINK_NOFOLLOW) != 0)
 +        {
 +          if (errno != ENOENT)
 +            {
 +              glnx_set_error_from_errno (error);
 +              goto out;
 +            }
 +          do_sync = (flags & GLNX_FILE_REPLACE_DATASYNC_NEW) > 0;
 +        }
 +      else
 +        do_sync = TRUE;
 +
 +      if (do_sync)
 +        {
 +          if (fdatasync (fd) != 0)
 +            {
 +              glnx_set_error_from_errno (error);
 +              goto out;
 +            }
 +        }
 +    }
 +
 +  if (uid != (uid_t) -1)
 +    {
 +      if (fchown (fd, uid, gid) != 0)
 +        {
 +          glnx_set_error_from_errno (error);
 +          goto out;
 +        }
 +    }
 +
 +  /* If a mode was forced, override umask */
 +  if (mode != (mode_t) -1)
 +    {
 +      if (fchmod (fd, mode) != 0)
 +        {
 +          glnx_set_error_from_errno (error);
 +          goto out;
 +        }
 +    }
 +
 +  if (renameat (dfd, tmppath, dfd, subpath) != 0)
 +    {
 +      glnx_set_error_from_errno (error);
 +      goto out;
 +    }
 +
 +  ret = TRUE;
 + out:
 +  return ret;
 +}
index a90544a9d80c2cc2a600e9f93f0d158aee8e1baf,0000000000000000000000000000000000000000..c0fd4e4832070f8e89b4310356baf4b18dcf9664
mode 100644,000000..100644
--- /dev/null
@@@ -1,121 -1,0 +1,124 @@@
 +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
 + *
 + * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org>.
 + *
 + * This library is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2 of the License, or (at your option) any later version.
 + *
 + * This library is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with this library; if not, write to the
 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 + * Boston, MA 02111-1307, USA.
 + */
 +
 +#pragma once
 +
 +#include <glnx-backport-autocleanups.h>
 +#include <limits.h>
 +#include <dirent.h>
 +#include <sys/stat.h>
 +#include <fcntl.h>
 +#include <string.h>
 +#include <sys/xattr.h>
 +/* From systemd/src/shared/util.h */
 +/* When we include libgen.h because we need dirname() we immediately
 + * undefine basename() since libgen.h defines it as a macro to the XDG
 + * version which is really broken. */
 +#include <libgen.h>
 +#undef basename
 +
 +G_BEGIN_DECLS
 +
 +/* Irritatingly, g_basename() which is what we want
 + * is deprecated.
 + */
 +static inline
 +const char *glnx_basename (const char *path)
 +{
 +  return (basename) (path);
 +}
 +
 +GBytes *
 +glnx_fd_readall_bytes (int               fd,
 +                       GCancellable     *cancellable,
 +                       GError          **error);
 +
 +char *
 +glnx_fd_readall_utf8 (int               fd,
 +                      gsize            *out_len,
 +                      GCancellable     *cancellable,
 +                      GError          **error);
 +
 +char *
 +glnx_file_get_contents_utf8_at (int                   dfd,
 +                                const char           *subpath,
 +                                gsize                *out_len,
 +                                GCancellable         *cancellable,
 +                                GError              **error);
 +
 +/**
 + * GLnxFileReplaceFlags:
 + * @GLNX_FILE_REPLACE_DATASYNC_NEW: Call fdatasync() even if the file did not exist
 + * @GLNX_FILE_REPLACE_NODATASYNC: Never call fdatasync()
 + *
 + * Flags controlling file replacement.
 + */
 +typedef enum {
 +  GLNX_FILE_REPLACE_DATASYNC_NEW = (1 << 0),
 +  GLNX_FILE_REPLACE_NODATASYNC = (1 << 1),
 +} GLnxFileReplaceFlags;
 +
 +gboolean
 +glnx_file_replace_contents_at (int                   dfd,
 +                               const char           *subpath,
 +                               const guint8         *buf,
 +                               gsize                 len,
 +                               GLnxFileReplaceFlags  flags,
 +                               GCancellable         *cancellable,
 +                               GError              **error);
 +
 +gboolean
 +glnx_file_replace_contents_with_perms_at (int                   dfd,
 +                                          const char           *subpath,
 +                                          const guint8         *buf,
 +                                          gsize                 len,
 +                                          mode_t                mode,
 +                                          uid_t                 uid,
 +                                          gid_t                 gid,
 +                                          GLnxFileReplaceFlags  flags,
 +                                          GCancellable         *cancellable,
 +                                          GError              **error);
 +
 +char *
 +glnx_readlinkat_malloc (int            dfd,
 +                        const char    *subpath,
 +                        GCancellable  *cancellable,
 +                        GError       **error);
 +
++int
++glnx_loop_write (int fd, const void *buf, size_t nbytes);
++
 +typedef enum {
 +  GLNX_FILE_COPY_OVERWRITE,
 +  GLNX_FILE_COPY_NOXATTRS,
 +  GLNX_FILE_COPY_DATASYNC
 +} GLnxFileCopyFlags;
 +
 +gboolean
 +glnx_file_copy_at (int                   src_dfd,
 +                   const char           *src_subpath,
 +                   struct stat          *src_stbuf,
 +                   int                   dest_dfd,
 +                   const char           *dest_subpath,
 +                   GLnxFileCopyFlags     copyflags,
 +                   GCancellable         *cancellable,
 +                   GError              **error);
 +
 +G_END_DECLS
index 9bd41c45decdf1754adfce96b825fb3c0f1f2fc1,0000000000000000000000000000000000000000..451740d2acbb805d5fb05d7c91348160448d1292
mode 100644,000000..100644
--- /dev/null
@@@ -1,39 -1,0 +1,40 @@@
 +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
 + *
 + * Copyright (C) 2012,2013,2015 Colin Walters <walters@verbum.org>.
 + *
 + * This library is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation; either
 + * version 2 of the License, or (at your option) any later version.
 + *
 + * This library is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with this library; if not, write to the
 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 + * Boston, MA 02111-1307, USA.
 + */
 +
 +#pragma once
 +
 +#include <gio/gio.h>
 +
 +G_BEGIN_DECLS
 +
++#include <glnx-alloca.h>
 +#include <glnx-local-alloc.h>
 +#include <glnx-backport-autocleanups.h>
 +#include <glnx-backports.h>
 +#include <glnx-lockfile.h>
 +#include <glnx-errors.h>
 +#include <glnx-dirfd.h>
 +#include <glnx-shutil.h>
 +#include <glnx-xattrs.h>
 +#include <glnx-libcontainer.h>
 +#include <glnx-console.h>
 +#include <glnx-fdio.h>
 +
 +G_END_DECLS